package jpaperwork.meta.transformer;

import jpaperwork.meta.descriptor.*;
import jpaperwork.meta.transformer.path.Path;

import java.util.List;
import java.util.ArrayList;


public class FormTransformer {


    final FormReader reader;
    final FormBuilder builder;


    final Form form;

    public FormTransformer(Form form, FormReader reader, FormBuilder builder) {
        this.builder = builder;
        this.form = form;
        this.reader = reader;
    }

    public Object transform(Object transformee){
        return transform(form, new Path(), transformee);

    }

    private Object transform(Type form, final Path path, final Object transformee) {
         return new TypeVisitor(){
             protected Object visitAggregation(Aggregation aggregation) {
                 List transformedValues = new ArrayList();
                 List values = reader.readCollectionValues(aggregation, transformee);
                 int index = 0;
                 for (Object value : values) {
                     transformedValues.add(transform(aggregation.getType(), path.aggregation(aggregation, index), value));
                     index++;
                 }

                 return builder.buildCollection(path, aggregation, transformedValues);
             }

             protected Object visitFlat(Flat flat) {                                    
                return builder.buildFlat(path, flat, transformee);
             }

             protected Object visitForm(Form form) {
                 List<FieldValue> transformedFields = new ArrayList<FieldValue>(form.getFields().size());
                 for (Field field : form.getFields()){
                    Object value = reader.readFieldValue(field, transformee);
                    transformedFields.add(new FieldValue(field, transform(field.getType(), path.field(field), value)));
                 }
                 return builder.buildForm(path, form, transformedFields);
             }

             protected Object visitPolymorhpism(Polymorphism polymorphism) {
                 final PolymorphicValue transformed = reader.determineType(polymorphism, transformee);
                 DiscriminatedType actualType = transformed.getType();
                 final Object transformedValue = transform(actualType.getForm(), path.polymorphic(polymorphism,  actualType), transformed.getValue());
                 return builder.buildPolymorhpic(path, polymorphism, actualType, transformedValue);
             }
         }.visit(form);

    }


}
